/*!
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.\n
            All Rights Reserved.\n
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file       
\ingroup    event_bus
\brief      Event Bus implementation
@{
*/

#include "event_bus_logging.h"
#include "event_bus_subscribe.h"
#include "event_bus_publish.h"
#include "event_bus_sniffer.h"

#include <panic.h>

#include <stdlib.h>
#include <stdio.h>

#define EVENT_BUS_LOG       DEBUG_LOG
#define EVENT_BUS_LOG_DATA  DEBUG_LOG_DATA

#define eventBus_SubscriberIsSame(subscriber_1, subscriber_2) \
    (((subscriber_1).handler == (subscriber_2).handler) && ((subscriber_1).context == (subscriber_2).context))

typedef struct
{
    event_bus_subscriber_t subscriber;
    event_bus_event_t *sniffer_events;
    uint8 num_sniffer_events;
}subscriber_info_t;

typedef struct
{
    event_bus_channel_t channel;
    uint8 number_of_subscribers;
    subscriber_info_t * subscribers;
} channel_registry_t;

typedef struct
{
    uint8 number_of_active_channels;
    channel_registry_t * active_channel_registries;
} subscriber_registry_t;

static event_bus_sniffer_handler_t channel_sniffer_event_handler = NULL;
static subscriber_registry_t subscriber_registry = { 0 };

static void eventBus_PrintRegistry(void)
{
    uint16 calculated_size = 0;
    EVENT_BUS_LOG("EventBus: active channels=%d", subscriber_registry.number_of_active_channels);
    for(int i = 0; i < subscriber_registry.number_of_active_channels; i++)
    {
        EVENT_BUS_LOG("EventBus:     channel=%d subscribers=%d",subscriber_registry.active_channel_registries[i].channel, subscriber_registry.active_channel_registries[i].number_of_subscribers);
        for(int j = 0; j < subscriber_registry.active_channel_registries[i].number_of_subscribers; j++)
        {
            event_bus_subscriber_t subscriber = subscriber_registry.active_channel_registries[i].subscribers[j].subscriber;
            EVENT_BUS_LOG("EventBus:         subscriber.handler=%p subscriber.context=%p", subscriber.handler, subscriber.context);
        }
        calculated_size += sizeof(channel_registry_t) + (sizeof(event_bus_channel_handler_t) * subscriber_registry.active_channel_registries[i].number_of_subscribers);
    }
    calculated_size += sizeof(subscriber_registry_t) + (sizeof(channel_registry_t) * subscriber_registry.number_of_active_channels);
    EVENT_BUS_LOG("EventBus: registry size=%d",calculated_size);
}

static channel_registry_t * eventBus_AddChannelRegistry(event_bus_channel_t channel)
{
    subscriber_registry.active_channel_registries = PanicNull(realloc(subscriber_registry.active_channel_registries, 
                    (sizeof(*subscriber_registry.active_channel_registries) * (subscriber_registry.number_of_active_channels+1))));
                    
    channel_registry_t * channel_registry = &subscriber_registry.active_channel_registries[subscriber_registry.number_of_active_channels];
    memset(channel_registry, 0, sizeof(*channel_registry));                
    channel_registry->channel = channel;
    
    subscriber_registry.number_of_active_channels++;
    return channel_registry;
}

static void eventBus_RemoveChannelRegistry(channel_registry_t * channel_registry)
{
    for(int i = 0; i < subscriber_registry.number_of_active_channels; i++)
    {
        if(&subscriber_registry.active_channel_registries[i] == channel_registry)
        {
            subscriber_registry.number_of_active_channels--;
            memcpy(&subscriber_registry.active_channel_registries[i], &subscriber_registry.active_channel_registries[i+1], 
                            (sizeof(*subscriber_registry.active_channel_registries) * (subscriber_registry.number_of_active_channels-i)));
            if(subscriber_registry.number_of_active_channels)
            {
                subscriber_registry.active_channel_registries = PanicNull(realloc(subscriber_registry.active_channel_registries, 
                                  (sizeof(*subscriber_registry.active_channel_registries) * subscriber_registry.number_of_active_channels)));
            }
            else
            {
                free(subscriber_registry.active_channel_registries);
                subscriber_registry.active_channel_registries = NULL;
            }
            break;
        }
    }
}

static channel_registry_t * eventBus_GetChannelRegistry(event_bus_channel_t channel)
{
    channel_registry_t * subscriptions = NULL;
    for(int i = 0; i < subscriber_registry.number_of_active_channels; i++)
    {
        if(subscriber_registry.active_channel_registries[i].channel == channel)
        {
            subscriptions = &subscriber_registry.active_channel_registries[i];
            break;
        }
    }
    return subscriptions;
}

static bool eventBus_DoesSubscriptionExist(channel_registry_t * channel_registry, event_bus_subscriber_t subscriber)
{
    PanicNull(channel_registry);
    bool subscription_found = FALSE;
    for(int i = 0; i < channel_registry->number_of_subscribers; i++)
    {
        if(eventBus_SubscriberIsSame(channel_registry->subscribers[i].subscriber, subscriber))
        {
            if(channel_registry->subscribers[i].subscriber.context == subscriber.context)
            {
                subscription_found = TRUE;
            }
        }
    }
    return subscription_found;
}

static subscriber_info_t* eventBus_GetSubscription(channel_registry_t * channel_registry, event_bus_subscriber_t subscriber)
{
    PanicNull(channel_registry);
    int sub_index = 0;
    for(; sub_index < channel_registry->number_of_subscribers; sub_index++)
    {
        if(eventBus_SubscriberIsSame(channel_registry->subscribers[sub_index].subscriber, subscriber))
        {
            break;
        }
    }
    PanicFalse(sub_index != channel_registry->number_of_subscribers); // Must exist
    return &channel_registry->subscribers[sub_index];
}

static inline bool eventBus_IsEventSnifferEvent(subscriber_info_t *subscriber, event_bus_event_t event)
{
    bool eventIsSnifferEvent = FALSE;
    for(int i = 0;i < subscriber->num_sniffer_events; i++)
    {
        if(subscriber->sniffer_events[i] == event)
        {
            eventIsSnifferEvent = TRUE;
            break;
        }
    }
    return eventIsSnifferEvent;
}

static inline void eventBus_PublishNonSnifferEventsToChannelRegistry(channel_registry_t * channel_registry, event_bus_channel_t channel, event_bus_event_t event, void * data, uint16 data_size)
{
    for(int i = 0; i < channel_registry->number_of_subscribers; i++)
    {
        if(!eventBus_IsEventSnifferEvent(&channel_registry->subscribers[i], event))
        {
            event_bus_subscriber_t subscriber = channel_registry->subscribers[i].subscriber;
            EVENT_BUS_LOG("    to subscriber=%p", channel_registry->subscribers[i].subscriber.handler);
            EVENT_BUS_LOG_DATA(data, data_size);
            subscriber.handler(channel, event, data, data_size, &subscriber);
        }
    }
}

static inline void eventBus_PublishSnifferEventsToChannelRegistry(channel_registry_t * channel_registry, event_bus_channel_t channel, event_bus_event_t event, void * data, uint16 data_size)
{
    for(int i = 0; i < channel_registry->number_of_subscribers; i++)
    {
        if(eventBus_IsEventSnifferEvent(&channel_registry->subscribers[i], event))
        {
            event_bus_subscriber_t subscriber = channel_registry->subscribers[i].subscriber;
            EVENT_BUS_LOG("    sniffered event to subscriber=%p", channel_registry->subscribers[i].subscriber.handler);
            EVENT_BUS_LOG_DATA(data, data_size);
            subscriber.handler(channel, event, data, data_size, &subscriber);
        }
    }
}

static void eventBus_PublishSnifferedEvent(event_bus_channel_t channel, event_bus_event_t sniffered_event, void * data, uint16 data_size)
{
    EVENT_BUS_LOG("EventBus_PublishSnifferedEvent channel=%d event=%d data_size=%d", channel, sniffered_event, data_size);
    channel_registry_t * channel_registry = eventBus_GetChannelRegistry(channel);
    if(channel_registry)
    {
        eventBus_PublishSnifferEventsToChannelRegistry(channel_registry, channel, sniffered_event, data, data_size);
    }
}

static inline void eventBus_SendSnifferEventsToSnifferHandler(channel_registry_t * channel_registry, event_bus_channel_t channel, event_bus_event_t event, void * data, uint16 data_size)
{
    for(int i = 0; i < channel_registry->number_of_subscribers; i++)
    {
        if(eventBus_IsEventSnifferEvent(&channel_registry->subscribers[i], event))
        {
            EVENT_BUS_LOG("    sniffered event to sniffer handler=%p", channel_sniffer_event_handler);
            PanicNull((void*)channel_sniffer_event_handler);
            channel_sniffer_event_handler(channel, event, data, data_size);

            /* Sniffer event handler must only be called once per sniffer event. The sniffer event handler will
            then snifferhronise the republishing of the sniffer event, and the event bus will iterate over the
            channels that have registered for sniffer events, publishing to each in turn */
            break;
        }
    }
}

static inline bool eventBus_IsNewSubscriptionRequired(channel_registry_t * channel_registry, event_bus_subscriber_t subscriber)
{
    return !channel_registry || 
        (channel_registry && !eventBus_DoesSubscriptionExist(channel_registry, subscriber));
}

void EventBus_Subscribe(event_bus_channel_t channel, event_bus_subscriber_t subscriber)
{
    PanicNull((void *)subscriber.handler);
    EVENT_BUS_LOG("EventBus_Subscribe");
    channel_registry_t * channel_registry = eventBus_GetChannelRegistry(channel);
    if(!channel_registry)
    {
        EVENT_BUS_LOG("    adding channel channel=%d", channel);
        channel_registry = eventBus_AddChannelRegistry(channel);
    }

    if(!eventBus_DoesSubscriptionExist(channel_registry, subscriber))
    {
        EVENT_BUS_LOG("    handler %p to channel %d", subscriber.handler, channel);
        channel_registry->subscribers = PanicNull(realloc(channel_registry->subscribers, 
                                            (sizeof(*channel_registry->subscribers) * (channel_registry->number_of_subscribers+1))));
        memset(&channel_registry->subscribers[channel_registry->number_of_subscribers], 0, sizeof(channel_registry->subscribers[channel_registry->number_of_subscribers]));
        channel_registry->subscribers[channel_registry->number_of_subscribers].subscriber = subscriber;
        channel_registry->number_of_subscribers++;
    }    

    if(EventBus_GetCurrentLoggingLevel() == DEBUG_LOG_LEVEL_V_VERBOSE)
    {
        eventBus_PrintRegistry();
    }
}

void EventBus_Unsubscribe(event_bus_channel_t channel, event_bus_subscriber_t subscriber)
{
    EVENT_BUS_LOG("EventBus_Unsubscribe");
    channel_registry_t * channel_registry = eventBus_GetChannelRegistry(channel);
    if(channel_registry && eventBus_DoesSubscriptionExist(channel_registry, subscriber))
    {
        for(int i = 0; i < channel_registry->number_of_subscribers; i++)
        {
            if(eventBus_SubscriberIsSame(channel_registry->subscribers[i].subscriber, subscriber))
            {
                EVENT_BUS_LOG("    handler %p and context %p from channel %d", subscriber.handler, subscriber.context, channel);
                channel_registry->number_of_subscribers--;
                memcpy(&channel_registry->subscribers[i], &channel_registry->subscribers[i+1], (sizeof(*channel_registry->subscribers) * (channel_registry->number_of_subscribers-i)));
                channel_registry->subscribers[channel_registry->number_of_subscribers].num_sniffer_events = 0;
                free(channel_registry->subscribers[channel_registry->number_of_subscribers].sniffer_events) ;             
                channel_registry->subscribers[channel_registry->number_of_subscribers].sniffer_events = NULL;
                if(channel_registry->number_of_subscribers)
                {
                    channel_registry->subscribers = PanicNull(realloc(channel_registry->subscribers, 
                                                (sizeof(*channel_registry->subscribers) * channel_registry->number_of_subscribers)));
                }
                else
                {
                    free(channel_registry->subscribers);
                    channel_registry->subscribers = NULL;
                    EVENT_BUS_LOG("    removing channel %d", channel_registry->channel);
                    eventBus_RemoveChannelRegistry(channel_registry);
                }
                break;
            }
        }
    }
    if(EventBus_GetCurrentLoggingLevel() == DEBUG_LOG_LEVEL_V_VERBOSE)
    {
        eventBus_PrintRegistry();
    }
}

void EventBus_UnsubscribeAll(void)
{
    for(int i = 0; i < subscriber_registry.number_of_active_channels; i++)
    {
        free(subscriber_registry.active_channel_registries[i].subscribers->sniffer_events);
        free(subscriber_registry.active_channel_registries[i].subscribers);
        memset(&subscriber_registry.active_channel_registries[i], 0, sizeof(channel_registry_t));
    }
    free(subscriber_registry.active_channel_registries);
    memset(&subscriber_registry, 0, sizeof(subscriber_registry_t));
}

void EventBus_Publish(event_bus_channel_t channel, event_bus_event_t event, void * data, uint16 data_size)
{
    EVENT_BUS_LOG("EventBus_Publish channel=%d event=%d data_size=%d", channel, event, data_size);
    channel_registry_t * channel_registry = eventBus_GetChannelRegistry(channel);
    if(channel_registry)
    {
        eventBus_SendSnifferEventsToSnifferHandler(channel_registry, channel, event, data, data_size);
        eventBus_PublishNonSnifferEventsToChannelRegistry(channel_registry, channel, event, data, data_size);
    }
}

void EventBus_RegisterEventSniffer(event_bus_sniffer_handler_t sniffer_event_handler, event_bus_sniffer_handler_t *publish_sniffer_event)
{
    PanicFalse(channel_sniffer_event_handler == NULL);
    channel_sniffer_event_handler = sniffer_event_handler;
    *publish_sniffer_event = eventBus_PublishSnifferedEvent;
}

void EventBus_UnregisterEventSniffer(event_bus_sniffer_handler_t sniffer_event_handler, event_bus_sniffer_handler_t* publish_sniffer_event)
{
    PanicFalse(channel_sniffer_event_handler == sniffer_event_handler);
    channel_sniffer_event_handler = NULL;
    *publish_sniffer_event = NULL;
}

void EventBus_SubscribeSnifferEvents(event_bus_channel_t channel, const event_bus_event_t sync_events[], uint8 num_sync_events, event_bus_subscriber_t subscriber)
{
    PanicNull((void*)channel_sniffer_event_handler);
    PanicFalse(num_sync_events);    
    PanicNull((void*)sync_events);

    channel_registry_t * channel_registry = eventBus_GetChannelRegistry(channel);
    if(eventBus_IsNewSubscriptionRequired(channel_registry, subscriber))
    {
        EventBus_Subscribe(channel, subscriber);
        channel_registry = eventBus_GetChannelRegistry(channel);
    }
    subscriber_info_t *subscriber_info = eventBus_GetSubscription(channel_registry, subscriber);
    subscriber_info->sniffer_events = PanicNull(realloc(subscriber_info->sniffer_events, 
                                                sizeof(sync_events[0]) * (subscriber_info->num_sniffer_events + num_sync_events)));
    memcpy(&subscriber_info->sniffer_events[subscriber_info->num_sniffer_events], sync_events, sizeof(event_bus_event_t) * num_sync_events);
    subscriber_info->num_sniffer_events += num_sync_events;
}

/*! @} End of group documentation */